package com.zlc.workflow.core.instance.cmd;

import com.zlc.common.core.process.model.vo.StoneProcessInstanceVo;
import com.zlc.common.core.process.model.vo.StoneProcessVo;
import com.zlc.common.core.process.model.vo.StoneTaskHistoryVo;
import com.zlc.common.core.process.model.vo.StoneTaskVo;
import com.zlc.common.core.process.service.*;
import com.zlc.common.core.user.model.vo.MayanPlatUserVo;
import com.zlc.common.core.user.service.MayanPlatUserService;
import com.zlc.common.util.AssertUtils;
import com.zlc.common.util.BeanCopyUtils;
import com.zlc.common.util.PlatUtils;
import com.zlc.workflow.constant.ProcessLogTypeEnum;
import com.zlc.workflow.core.instance.model.EventParam;
import com.zlc.workflow.core.instance.model.QueryRejectNodeParam;
import com.zlc.workflow.core.instance.model.RejectFlowParam;
import com.zlc.workflow.core.instance.model.RejectNodeVo;
import com.zlc.workflow.core.instance.model.WorkflowDetail;
import com.zlc.workflow.core.instance.model.WorkflowTask;
import com.zlc.workflow.core.instance.service.EventService;
import com.zlc.workflow.flow.NodeModel;
import com.zlc.workflow.flow.NodeUtils;
import com.zlc.workflow.flow.node.TaskNode;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.ObjectUtils;

import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static com.zlc.common.common.constant.WorkflowConstant.EVENT_REJECT_NOW_TASK;
import static com.zlc.common.common.constant.WorkflowConstant.EVENT_TASK_BE_REJECT;
import static com.zlc.common.common.constant.WorkflowConstant.TASK_OPERATE_TYPE_REJECT;
import static com.zlc.common.common.constant.WorkflowConstant.TASK_STATUS_COMPLETE;
import static com.zlc.common.common.constant.WorkflowConstant.TASK_STATUS_PROCESSING;

@Slf4j
public class RejectCmd extends AbstractCmd<WorkflowDetail>{

    @Autowired
    private MayanPlatUserService mayanPlatUserService;
    @Autowired
    private StoneProcessService stoneProcessService;
    @Autowired
    private StoneProcessInstanceService stoneProcessInstanceService;
    @Autowired
    private StoneTaskService stoneTaskService;
    @Autowired
    private StoneTaskHistoryService stoneTaskHistoryService;
    @Autowired
    private EventService eventService;
    @Autowired
    private StoneProcessLogService stoneProcessLogService;
    @Autowired
    private StoneProcessVariableConfigService stoneProcessVariableConfigService;

    private final RejectFlowParam workflowParam;

    private final String account;

    private final  String processId;

    private final MayanPlatUserVo nowUser;

    private final String processInstanceId;

    private final String taskId;

    private final String rejectNodeId;

    private final StoneProcessVo processVo;

    private final StoneTaskVo taskVo;

    private final StoneProcessInstanceVo instanceVo;

    private boolean taskNextContinue = false;

    private boolean changeInstanceFlag = false;

    private final TaskNode taskNode;

    private final TaskNode rejectTaskNode;

    private final  WorkflowDetail result = new WorkflowDetail();
    /**
     * 配置变量
     */
    private Map<String,String> variableConfigs = new HashMap<>();

    private List<String> rejectTaskIds;

    /**
     * 获取当前命令对象的所有带 @Autowired注解的属性，然后从spring上下文中获取到注入进去
     *
     * @param params
     */
    public RejectCmd(Object params) {
        super(params);
        this.workflowParam = (RejectFlowParam) params;
        this.processId = workflowParam.getProcessId();
        this.account = workflowParam.getAccount();
        this.processInstanceId = workflowParam.getProcessInstanceId();
        this.taskId = workflowParam.getTaskId();
        this.rejectNodeId = workflowParam.getRejectNodId();
        AssertUtils.isTrue(!ObjectUtils.isEmpty(this.workflowParam.getSuggest()),"审批意见不可为空！");
        AssertUtils.isTrue(!ObjectUtils.isEmpty(processInstanceId),"流程实例ID缺失");
        AssertUtils.isTrue(!ObjectUtils.isEmpty(taskId),"流程任务ID缺失");
        AssertUtils.isTrue(!ObjectUtils.isEmpty(rejectNodeId),"退回任务节点缺失");
        this.instanceVo = stoneProcessInstanceService.getByProcessInstanceId(processInstanceId);
        AssertUtils.isTrue(!ObjectUtils.isEmpty(instanceVo),"流程实例ID错误");
        this.taskVo = stoneTaskService.getByTaskId(taskId);
        AssertUtils.isTrue(!ObjectUtils.isEmpty(taskVo),"流程任务ID错误");

        result.setProcessInstanceId(processInstanceId);
        AssertUtils.isTrue(!ObjectUtils.isEmpty(workflowParam.getAccount()),"操作人不可为空");
        this.nowUser = mayanPlatUserService.queryByUserAccount(workflowParam.getAccount());
        AssertUtils.isTrue(!ObjectUtils.isEmpty(this.nowUser),"操作人不存在");
        AssertUtils.isTrue(account.equals(taskVo.getActorId()),"无权操作");

        this.processVo = stoneProcessService.getByProcessId(processId);
        String content = NodeUtils.getProcessContent(processVo.getContent());
        this.taskNode = NodeUtils.getTaskNode(content, processId, taskVo.getNodeId());
        this.rejectTaskNode = NodeUtils.getTaskNode(content,processId,rejectNodeId);

        this.rejectTaskIds = this.workflowParam.getRejectTaskIds();
        AssertUtils.isTrue(!ObjectUtils.isEmpty(rejectTaskIds),"退回任务缺失");
    }

    @Override
    public WorkflowDetail execute() {
        checkReject();
        //处理当前任务
        completeNowTask();
        //执行退回
        rejectTask();

        loadWorkflowDetail();
        return this.result;
    }

    private void checkReject(){
        QueryRejectNodeParam nodeParam = BeanCopyUtils.copyObj(this.workflowParam, QueryRejectNodeParam.class);
        List<RejectNodeVo> rejectNodeVos = new QueryRejectNodeCmd(nodeParam).execute();
        RejectNodeVo rejectNodeVo = rejectNodeVos.stream().filter(r -> r.getNodeId().equals(rejectNodeId)).findFirst().orElse(null);
        AssertUtils.isTrue(!ObjectUtils.isEmpty(rejectNodeVo),"节点【"+rejectNodeId+"】无权退回");
    }

    private void loadWorkflowDetail(){
        WorkflowTask details = BeanCopyUtils.copyAndInit(instanceVo,WorkflowTask.class);
        details.setProcessCode(processVo.getProcessCode());
        details.setProcessName(processVo.getProcessName());
        details.setInitAccount(instanceVo.getStarter());
        details.setInitUserName(instanceVo.getStarterName());
        this.result.setDetails(details);
    }

    /**
     * 执行退回
     */
    private void rejectTask(){
        for(String taskId : this.rejectTaskIds){
            StoneTaskHistoryVo historyVo = stoneTaskHistoryService.getByTaskId(taskId);
            AssertUtils.isTrue(!ObjectUtils.isEmpty(historyVo),"退回信息错误->"+taskId);
            //生成新的任务
            StoneTaskVo taskVo = BeanCopyUtils.copyAndInit(historyVo,StoneTaskVo.class);
            taskVo.setReceiveTime(new Date());
            taskVo.setStatus(TASK_STATUS_PROCESSING);
            taskVo.setTaskId(PlatUtils.uuid());
            stoneTaskService.saveVo(taskVo);
            //保存新任务历史记录
            StoneTaskHistoryVo newHistoryVo = BeanCopyUtils.copyAndInit(taskVo,StoneTaskHistoryVo.class);
            stoneTaskHistoryService.saveVo(newHistoryVo);
        }
        //任务被退回时，执行回调
        eventExecute(this.rejectTaskNode,EVENT_TASK_BE_REJECT);
    }

    private void completeNowTask(){
        //保存历史任务
        StoneTaskHistoryVo historyVo = stoneTaskHistoryService.getByTaskId(taskId);
        historyVo.setExecuteTime(new Date());
        historyVo.setStatus(TASK_STATUS_COMPLETE);
        historyVo.setOperateType(TASK_OPERATE_TYPE_REJECT);
        historyVo.setActorId(account);
        historyVo.setActorName(nowUser.getUsername());
        historyVo.setSuggest(this.workflowParam.getSuggest());
        stoneTaskHistoryService.saveVo(historyVo);
        //删除进行中任务
        stoneTaskService.deleteObjById(taskVo.getId());
        //退回当前任务时，执行回调
        eventExecute(taskNode,EVENT_REJECT_NOW_TASK);
        //记录日志
        stoneProcessLogService.saveLog(processInstanceId,processId,taskVo.getNodeId(),taskVo.getNodeName(),taskVo.getTaskId(),
                ProcessLogTypeEnum.REJECT.getType(),
                this.account,"审批退回了任务，审批节点【"+taskNode.nodeName()+"】，退回节点【"+this.rejectTaskNode.nodeName()+"】");
    }


    private void eventExecute(NodeModel nodeModel, String eventTime){
        log.info("流程{}，执行自动处理前置回调",processInstanceId);
        //获取配置变量
        this.variableConfigs = stoneProcessVariableConfigService.getVariableConfigs(processId,nodeModel.nodeId());
        EventParam eventParam = new EventParam();
        eventParam.setProcessCode(processVo.getProcessCode());
        eventParam.setProcessId(processId);
        eventParam.setActorId(this.account);
        eventParam.setActorName(this.nowUser.getUsername());
        eventParam.setEventTime(eventTime);
        eventParam.setNodeId(nodeModel.nodeId());
        eventParam.setNodeName(nodeModel.nodeName());
        eventParam.setNodeType(nodeModel.nodeType().value());
        eventParam.setProcessInstanceId(instanceVo.getProcessInstanceId());
        eventParam.setVariableConfigs(variableConfigs);
        eventService.execute(eventParam);
    }

}
